; ORIGINAL: 3dt2.1/examples/movepath.scm
; $Id: movepath.scm,v 1.4 1997/04/30 20:24:14 jread Exp $
;----------------------------------------------------------------------
; Purpose---
;    Provide procedures to animate an object by moving it along a path.
;
;----------------------------------------------------------------------

;; This procedure puts attributes on an entity that you want to move along
;; a path.  The position provided is the position on the body that
;; you want to follow the curve.  The gvector defines the direction that
;; you want to face forward along the curve
(define (setup-body body pos dir)
  (attrib:replace body "center" pos)
  (attrib:replace body "front" dir)
  )

;; Move an entity along a curve
;;   ent  = the entity that you want to move
;;   path = a curve that you want to move it along
;;   num  = the number of steps (must be > 0)
;; You should call setup-body before calling this procedure to make
;; sure that the entity being moved has the right attributes attached.
(define (move-path ent path num)
  ; Define some local procedures which will be used to move the entity
  ; along the path
  (let* ((LastTransform (transform:identity))
         (move-to-position  ; move the entity to a given position
           (lambda (ent pos dir)
             (let ((to-model (transform:inverse LastTransform)))
               (set! LastTransform
                     (transform:axes pos dir (gvector:cross dir (z-axis))))
               (entity:transform ent (transform:compose to-model LastTransform))
               )))
         (move-on-curve    ; move the entity to a given position on the curve
           (lambda (ent crv t)
             (move-to-position ent
                               (curve:eval-pos crv t)
                               (curve:eval-tan crv t))))
         (get-center       ; get the "center" of the entity
           (lambda (ent)
             (let ((entity-center
                     (lambda (ent)
                       (let ((box (entity:box ent)))
                         (position:interpolate (car box) (cdr box) .5))))
                   (attrib (attrib:get ent "center"))
                   )
               (if (not (null? attrib))
                   (let ((pos (cdar attrib)))
                     (if (position? pos)
                         pos
                         (entity-center ent)
                         ))
                   (entity-center ent)
                   ))))
         (get-front       ; get the "front" of the entity
           (lambda (ent)
             (let ((attrib (attrib:get ent "front")))
               (if (not (null? attrib))
                   (let ((dir (cdar attrib)))
                     (if (gvector? dir)
                         dir
                         (gvector 1 0 0)
                         ))
                   (gvector 1 0 0)
                   ))))
         )

    ; Initialize the transform to move the entity to the start of the curve
    (set! LastTransform
          (let ((pos (get-center ent))
                (dir (get-front ent))
                )
            (transform:axes pos dir (gvector:cross dir (z-axis)))))

    ; Get the start and end parameters of the curve
    (let* ((t0 (curve:start-param path))
           (t1 (curve:end-param path))
           (dt (/ (- t1 t0) num))
           )

      ; Loop to move the entity along the curve
      (let loop ((i 0))
        (if (<= i num)
            (let ((t (+ t0 (* i dt))))
              (move-on-curve ent path t)
              (loop (+ i 1))
              )))
      )))
